package symbol;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Stack;

import syntaxtree.Type;

public class Table {
	
	private Dictionary< Symbol , Stack<Type> > dict = new Hashtable< Symbol , Stack<Type> >();
	private Stack<Symbol> stack = new Stack<Symbol>();
	private Symbol begin = Symbol.symbol("#BEGIN_SCOPE");
	
	public Table() {
	}
	
	public void put(Symbol key, Object value) throws Exception {
		
		// Verifica dupla declaração
		int scopebegins = stack.search(begin);
		int keyishere = stack.search(key);
		if( keyishere < scopebegins ){
			throw new Exception("Dupla declaração de variável.");
		}
		
		// Acrescenta o tipo na extrutura externa do hash
		Stack<Type> pilha = dict.get(key);
		if( pilha == null )
		{
			pilha = new Stack<Type>();
			dict.put(key, pilha);
		}
		
		// Acrescenta o simbolo do identificador a pilha auxiliar
		pilha.push((Type)value);
		stack.push(key);
	}
	
	public Object get(Symbol key) throws Exception {
		Stack<Type> pilha = dict.get(key);
		if( pilha == null ) return null;
		if( pilha.isEmpty() ) return null;
		return (Object)pilha.firstElement();
	}
	
	public void beginScope() {
		stack.push(begin);
	}
	public void endScope() {
		Symbol remove;
		Stack<Type> pilha;
		while( !stack.isEmpty() )
		{
			// Remove todos os elementos no escopo corrente
			remove = stack.pop();
			if( remove == begin ){
				break;
			} else {
				// Remove o elemento no topo do encadeamento externo
				pilha = dict.get(remove);
				pilha.pop();
			}
		}			
	}
	
	public java.util.Enumeration<Symbol> keys() {
		// Retorna os identificadores correntes
		return dict.keys();
	}
	
}